////////////////////////////////////////////////////////////////////////////////
//
// Arp Rich Internet Application Framework (http://osflash.org/arp)
// Copyright @copy; 2004-2006 Aral Balkan (http://flashant.org)
// 
// Released under the open-source MIT license.
// (http://www.opensource.org/licenses/mit-license.php)
//
// See license.txt for full license terms.
//
////////////////////////////////////////////////////////////////////////////////

package org.osflash.arp
{
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.events.Event;
	
	// Is there an AS3 verion of the LuminicBox Logger?
	// import org.osflash.arp.log.Log;
	
	/**
	* <p><b>The ControllerTemplate provides base functionality for your Controller
	* classes.</b></p> 
	* 
	* <p>Your ApplicationController and any controllers for external forms (SWFs)
	* should extend the ControllerTemplate and implement its primitive operations.</p>
	* 
	* <p><i>Copyright &copy; 2004-2006 <a href="http://flashant.org">Aral Balkan</a>.
	* Arp lives at <a href="http://osflash.org/arp">http://osflash.org/arp</a>. 
	* 
	* Released under the open-source MIT license.
	* (http://www.opensource.org/licenses/mit-license.php)
	* See license.txt for full license terms.</i></p>
	*/
	public class ControllerTemplate 
	{
		//////////////////////////////////////////////////////////////////////
		//
		// Properties
		//
		//////////////////////////////////////////////////////////////////////
			
		/**
		* Reference to the View. Use this instead of "app" in new
		* applications as you will have more than one controller
		* if you use external SWFs (screens) in your Flex and
		* Flash applications.
		*/
		private var view:Object = null; 
		
		/**
		* Deprecated: Reference to the application instance. Use the
		* view reference instead.
		*/ 
		protected var app:*; 
		
		/**
		* Holds named list of commands
		*/
		private var commands:Object; 	
		
		//////////////////////////////////////////////////////////////////////
		//
		// Constructor
		//
		//////////////////////////////////////////////////////////////////////
		function ControllerTemplate ()
		{
			// Initialize properties
			commands = new Object();
		}
		
		//////////////////////////////////////////////////////////////////////
		//
		// Public Methods
		//
		//////////////////////////////////////////////////////////////////////
	
		/**
		* Just calls registerApp() currently. Use in place of registerApp()
		* 
		* @param	screen	Reference to the screen that the controller should
		*                   listen for system events on.
		*/
		public function registerScreen ( screen:Object ):void
		{
			registerApp ( screen );
		}
	
		/**
		* Allows a view to register itself with the controller so that the 
		* controller can listen for system events on it. 
		* 
		* A view should only register once with the controller as this process
		* involves the addition of system event listeners as well as the 
		* addition of the commands that they will be mapped to to the controller.
		* 
		* @param	view	Reference to the view that the controller should
		* 					register to listen for system events on.
		*/
		public function registerApp ( view:* ):void
		{
			// store view reference
			this.view = view;
			
			// store application reference (deprecated)
			this.app = view;
			
			// add view event listeners manually
			// (hook operation)
			addEventListeners();
			
			// add commands manually
			// (hook operation)
			addCommands();
		}
	
		////////////////////////////////////////////////////////////////
		//
		// Public Event Handlers
		//
		////////////////////////////////////////////////////////////////
			
		/**
		* The handleEvent method gets called whenever any system event
		* is broadcast by the view. 
		* 
		* It uses a naming convention (NameOfEvent + Command) to 
		* find the command that maps to the event and execute it.
		* 
		* @param	eventObj	An event object
		*/
		public function handleEvent ( eventObj:Event ):* 
		{
			var eventName:String = eventObj.type;
			var commandNameToCheck:String = eventName + "Command";
			
			// Reference to the object that generated the event
			var viewRef:* = eventObj.target;
			
			if ( commands [ commandNameToCheck ] == undefined )
			{
				throw new Error ("ERROR ControllerTemplatehandleEvent - Unknown command called: "+commandNameToCheck+" from "+viewRef+". Please add this command to the Controller in its addCommands() method.");
			}
			else
			{
				// Create a new command
				var theCommand:* = new commands [ commandNameToCheck ] ();
				
				// Execute the command 
				theCommand.execute( viewRef, eventObj );
			}			
		}

		/**
		* Primitive operation. Provide singleton access to the Controller.
		* 
		* The class that subclasses ControllerTemplate must be a singleton
		* and must thus override this getInstance() method.
		*/
		public static function getInstance ():ControllerTemplate
		{
			throw new Error (
				"ERROR ControllerTemplate - getInstance primitive operation has not been implemented."
				+ "\nThe Application Controller must be a singleton and provide a concrete implementation of getInstance."
			);
		}
	
	
		////////////////////////////////////////////////////////////////////////////
		//
		// Private Methods
		//
		////////////////////////////////////////////////////////////////////////////
		
		/**
		* Concrete operation. Registers a command with the controller
		* 
		* TODO: Refactor this so that it implements the ICommand interface and
		* returns an ICommand. 
		* 
		* @param	commandName	The name of the command
		* @param	commandRef	Reference to the command class
		*/
		protected function addCommand ( commandName:String, commandRef:Class)
		{
			// Log.it ("ControllerTemplate.addCommand() - Command added:"+commandName+".");
			
			if ( commands [ commandName ] != undefined ) 
			{
				throw new Error ("ERROR The command "+commandName+" has already been added to the Controller.");
			}
			else
			{
				commands [ commandName ] = commandRef;
			}
		}	
	
		
		/**
		* Primitive operation. Override this in your Controller class and add
		* your system event listeners here.
		* 
		* Different screens may dispatch the same system event and these will be
		* handled by the same event handler. (No two screens should use the same 
		* system event for different purposes.)
		* 
		* The general format for adding listeners should be similar to:
		* 
		* {@code 
		* 	var queryScreen:QueryScreen = QueryScreen ( app.getScreen( app.QUERY_SCREEN ) );
		* 	queryScreen.addEventListener( "getPersonList", this );
		* }
		*/
		protected function addEventListeners ():void
		{	
			throw new Error ( 
				"ERROR ControllerTemplate - addEventListeners() primitive operation not implemented."
				+ "\nEvents have not been added to controller" 
			);
		}
		
		/**
		* Primitive operation. Override this in your Controller class and 
		* register your commands here using the addCommand() method.
		* 
		* Commands are added as references to the classes. Dispatching 
		* a command includes creating an instance of it, which is then kept
		* in a history log on the controller. The command is automatically 
		* removed after the service has returned (so we don't have a memory leak).
		* This allows a single command to be called from multiple views.
		* 
		* The general format should be:
		* 
		* {@code
		* 	addCommand ( "getPersonListCommand", GetPersonListCommand );
		* }
		*/
		protected function addCommands ():void
		{
			throw new Error ( 
				"ERROR ControllerTemplate - addCommands() primitive operation not implemented."
				+ "\nCommands not added to controller" 
			);
		}
	}
}